package org.budo.filestore.qiniu;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

import org.budo.filestore.api.DeleteResponse;
import org.budo.filestore.api.DownloadRequest;
import org.budo.filestore.api.FileMeta;
import org.budo.filestore.api.UploadRequest;
import org.budo.filestore.api.UploadResponse;
import org.budo.filestore.impl.DefaultDeleteResponse;
import org.budo.filestore.impl.DefaultUploadResponse;
import org.budo.filestore.qiniu.util.QiniuUtil;
import org.budo.support.http.client.Request;
import org.budo.support.java.net.util.UrlEncodeUtil;
import org.budo.support.lang.util.IoUtil;
import org.budo.support.lang.util.StringUtil;
import org.springframework.util.Assert;

import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import com.qiniu.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 * 
 *         https://developer.qiniu.com/kodo/manual/1202/download-token
 */
@Slf4j
public class QiniuFileStore extends AbstractQiniuFileStore {
    @Override
    public String uploadToken(UploadRequest request) {
        Assert.hasText(this.getAccessKey(), "#135 accessKey is null, this=" + this);
        Assert.hasText(this.getSecretKey(), "#136 secretKey is null, this=" + this);

        Auth auth = Auth.create(this.getAccessKey(), this.getSecretKey());
        return auth.uploadToken(this.getBucketName(), request.getContentUrl());
    }

    /**
     * https://developer.qiniu.com/kodo/kb/1326/how-to-upload-photos-to-seven-niuyun-base64-code
     */
    @Override
    public UploadResponse upload(UploadRequest request) {
        if (null == request || null == request.getContentUrl() || null == request.getInputStream()) {
            log.error("#51 qiniu upload error, request = " + request);
            return null;
        }

        try {
            String uploadToken = this.uploadToken(request);
            Assert.hasText(uploadToken, "#39 token=" + uploadToken + ", this=" + this);

            Configuration configuration = new Configuration();
            UploadManager uploadManager = new UploadManager(configuration);

            byte[] byteArray = IoUtil.toByteArray(request.getInputStream());
            String contentUrl = request.getContentUrl();
            Response qiniuResponse = uploadManager.put(byteArray, contentUrl, uploadToken);

            if (qiniuResponse.isOK()) {
                log.debug("#58 upload ok, qiniuResponse=" + qiniuResponse + ", request=" + request);
                return new DefaultUploadResponse(true);
            }

            log.error("#62 upload fail, qiniuResponse=" + qiniuResponse + ", request=" + request);
            return new DefaultUploadResponse(false);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 时间戳防盗链下载和私有资源下载，两者的效果相似，但推荐使用时间戳
     * https://developer.qiniu.com/fusion/kb/1670/timestamp-hotlinking-prevention
     * https://developer.qiniu.com/kodo/manual/1656/download-private
     */
    @Override
    public String downloadUrl(DownloadRequest request) {
        if (null == request || null == request.getContentUrl()) {
            log.error("#86 downloadUrl, request = " + request + ", this=" + this);
            return null;
        }

        Boolean https = "true".equals(request.getParameter("https") + "");
        String scheme = https ? "https" : "http";

        // 域名
        String bucketDomain = this.getBucketDomain();
        if (request instanceof QiniuDownloadRequest //
                && null != ((QiniuDownloadRequest) request).getDownloadDomain()) {
            bucketDomain = ((QiniuDownloadRequest) request).getDownloadDomain();
        }

        Assert.hasText(bucketDomain, "#85 bucketDomain=" + bucketDomain + ", this=" + this);

        // String contentUrl = UrlEncodeUtil.encode(request.getContentUrl());
        String contentUrl = request.getContentUrl();

        // 兼容 bucketDomain 以 http 开头
        String qiniuUrl = "";
        if (null != bucketDomain && (bucketDomain.startsWith("http://") || bucketDomain.startsWith("https://"))) {
            qiniuUrl += bucketDomain + "/" + contentUrl;
        } else {
            qiniuUrl += scheme + "://" + bucketDomain + "/" + contentUrl;
        }

        Object attname = request.getParameter("attname");

        if (null != attname && !StringUtils.isNullOrEmpty(attname.toString())) {
            qiniuUrl += "?attname=" + UrlEncodeUtil.encode(attname.toString());
        }

        // 未配置防盗链，直接下载
        if (StringUtil.isBlank(this.getAntiLeechKey())) {
            return qiniuUrl;
        }

        // 防盗链下载
        Integer antiLeechDuration = this.getAntiLeechDuration();
        if (request instanceof QiniuDownloadRequest //
                && null != ((QiniuDownloadRequest) request).getAntiLeechDuration()) {
            antiLeechDuration = ((QiniuDownloadRequest) request).getAntiLeechDuration();
        }

        return QiniuUtil.antiLeechAccessUrl(this.getAntiLeechKey(), antiLeechDuration, qiniuUrl);
    }

    @Override
    public InputStream downloadStream(DownloadRequest request) {
        String downloadUrl = this.downloadUrl(request);

        Request httpRequest = new Request();
        httpRequest.setUrl(downloadUrl);
        org.budo.support.http.client.Response response = httpRequest.execute();
        return new ByteArrayInputStream(response.getResponseData());
    }

    @Override
    public DeleteResponse delete(DownloadRequest request) {
        try {
            String accessKey = this.getAccessKey();
            Assert.hasText(accessKey, "#129 this.accessKey is null or empty, this=" + this);

            String secretKey = this.getSecretKey();
            Assert.hasText(secretKey, "#132 this.secretKey is null or empty, this=" + this);

            String bucketName = this.getBucketName();
            Assert.hasText(bucketName, "#135 this.bucketName is null or empty, this=" + this);

            String contentUrl = request.getContentUrl();
            Assert.hasText(contentUrl, "#138 request.contentUrl is null or empty, this=" + this);

            Auth auth = Auth.create(accessKey, secretKey);
            Configuration configuration = new Configuration();
            BucketManager bucketManager = new BucketManager(auth, configuration);
            Response response = bucketManager.delete(bucketName, contentUrl);
            return new DefaultDeleteResponse(response.isOK());
        } catch (QiniuException e) {
            if (612 == e.code()) { // 指定资源不存在或已被删除https://developer.qiniu.com/fusion/kb/1352/the-http-request-return-a-status-code
                log.error("#147 delete error, e=" + e + ", request=" + request);
                return new DefaultDeleteResponse(false);
            }

            throw new RuntimeException("#150 delete error, e=" + e + ", request=" + request, e);
        }
    }

    @Override
    public FileMeta getFileMeta(DownloadRequest request) {
        return new LazyLoadQiniuFileMeta(this, request);
    }
}
